/*
	PlAr is Platform Arena: a 2D multiplayer shooting game
	Copyright (c) 2010, Antonio Ragagnin <spocchio@gmail.com>
	All rights reserved.

	This file is licensed under the New BSD License.
*/

package plar.ClientServer;

import java.awt.Point;
import java.awt.Rectangle;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;

import java.net.SocketAddress;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;
import java.util.Arrays;

import org.jbox2d.common.Vec2;

import plar.ClientServer.Signal.Info;
import plar.ClientServer.Signal.LevelInfo;
import plar.ClientServer.Signal.Login;

import plar.core.Common;
import plar.core.KeyFlag;
import plar.core.SpriteSet;

import plar.core.Sprite;

public class Client extends Thread {
	private ArrayList<SpriteSet> buffer;
	DatagramSocket socket;

	List<ShownElement> screen;
	String guns;
	public Point resolution;

	String _server;
	Integer _port;
	String nick;
	String MOTD;
	String serverM;
	String type;
	public Vec2 scale;
	public boolean isConnected = false;
	public boolean isLogged = false;
	public String error;
	int ID;
	int Key;
	KeyFlag lastPremuti;
	SocketAddress addr;
	String messaggi;
	String chat;
	public boolean isBusy;
	public int spawns;
	public float life;
	public int score;
	public int currentgun;
	public int msTimeout=60*1000;
	public long remaning=0;

	public Client() throws SocketException {
		serverM="";
		resolution = new Point(Common.resx, Common.resy);
		lastPremuti = new KeyFlag((short) 0);
		buffer = new ArrayList<SpriteSet>();
		guns = new String();
		isBusy = false;
		scale = new Vec2();
		chat = new String();
		socket = new DatagramSocket();
		screen = new ArrayList<ShownElement>();
		messaggi = new String();
	}

	public synchronized String connect(String server, int port) throws IOException,
	ClassNotFoundException {
		Common.info(1, "Connecting to " + server + "...");
		addr = new InetSocketAddress(InetAddress.getByName(server), Common.port);

		byte[] data = new byte[10];
		DatagramPacket dp = new DatagramPacket(data, 0, data.length, addr);
		socket.send(dp);
		Common.info(7, "waiting4the login");
		socket.setSoTimeout(msTimeout);

		try {
			socket.receive(dp);
		} catch (Exception ex) {
			throw new IOException();
		}
		data = dp.getData();
		Key = data[1];
		ID = data[0];
		isConnected=true;
		//this.start();
		return "logged with ID=" + ID + " and KEY=" + Key+ "\n";
	}

	public void close() throws IOException {
		sendFlag(Signal.STOP);
		this.interrupt();
	}

	@SuppressWarnings("unchecked")
	public void run() {
		while (isConnected) {
			step();
		}
	}
	
	@SuppressWarnings("unchecked")
	public void step()
	{   byte[] data = null;
		try {

			data = new byte[socket.getReceiveBufferSize()];
			DatagramPacket dp = new DatagramPacket(data, data.length);
			try
			{
				socket.receive(dp);
			} catch (Exception ex) {
				isConnected=false;
			}
			data = Arrays.copyOf(data, dp.getLength() + 1);
			isBusy = true;
		} catch (IOException ex) {
			ex.printStackTrace();
		}

		byte signal = Signal.getSignal(data);
		data = Signal.getData(data);
		if (signal == Signal.SPRITE) {
			SpriteSet o = null;

			try {

				o = (SpriteSet) Signal.toObject(data);
			} catch (IOException ex) {
				ex.printStackTrace();
			} catch (ClassNotFoundException ex) {
				ex.printStackTrace();
			}
			if (o != null)
			buffer.add(o);
			Common.info(1,"new SpriteSet :"+o.get(0).getFileName());
		}
		if (signal == Signal.SCREEN) {
			synchronized (screen)
			{
				screen.clear();
				// Common.info(7,"parsing screen"+data.length);
				//int N = 5;
				int N = 4;
				int dimE=2;
				
				for (int i = 0; i < (int) (data.length / (N*2)); i++) {

					/*
			byte[] z = new byte[N + 1];
				z[0] = (int)data[0 + i * N];
				z[1] = (int)data[1 + i * N];
				z[2] = (int)data[2 + i * N];
				z[3] = (int)data[3 + i * N];
				z[4] = data[4 + i * N];
				z[5] = 0;
			*//*
				short[] z = new short[N + 1];
				z[0] = (short)pezzi[0 + i * N];
				z[1] = (short)pezzi[1 + i * N];
				z[2] = (short)pezzi[2 + i * N];
				z[3] = (short)pezzi[3 + i * N];
				/*z[4] = data[4 + i * N];
				z[5] = 0;
				*/
					
					// int[] dati = Signal.from6ByteTo5Int(z);
					/*int x = dati[0];
				int y = dati[1];
				int t = dati[2];
				int d = dati[3];
				float theta = t / 100f;*/
					/*
				int x=(int)z[0];
				int y=(int)z[1];
				int t=(int)z[2];
				int d=(int)z[3];*/
					
					
					int x=(int) Signal.from2ByteTo1Short(data[0 + i * N*2],data[0 + i * N*2 +1]);
					int y=(int)  Signal.from2ByteTo1Short(data[1*2 + i * N*2 ],data[1*2 + i * N*2 + 1]);				
					int t=(int)  Signal.from2ByteTo1Short(data[2*2 + i * N*2 ],data[2*2 + i * N*2 + 1]);
					int d=(int)  Signal.from2ByteTo1Short(data[3*2 + i * N*2 ],data[3*2 + i * N*2 + 1]);/*
				Common.info(1,"sending: "+x+" "+y+" "+d+" "+t);
				Common.info(1," d="	+Signal.from2ByteTo1Short(data[1*2 + i * N*2 ],data[1*2 + i * N*2 + 1])+" ; "
									+Signal.from2ByteTo1Short(data[1*2 + i * N*2 +1],data[1*2 + i * N*2 ])+" ;"+d);
			Common.info(1," t="	+Signal.from2ByteTo1Short(data[3*2 + i * N *2],data[3*2 + i * N*2 + 1])+" ; "
									+Signal.from2ByteTo1Short(data[3*2 + i * N*2 +1],data[3*2 + i * N*2 ])+" ;"+t);
									
									
									Common.info(1,"sending: "+x+":["+data[0 + i * 2*N]+" "+data[1 + i *2* N]+"]"
										+y+":["+data[2 + i *2* N]+" "+data[3 + i *2* N]+"]"
										+t+":["+data[4 + i *2* N]+" "+data[5 + i *2* N]+"]"
										+d+":["+data[6 + i *2* N]+" "+data[7 + i *2* N]+"]"
										);
										*/
					/*				Common.info(1," s="	+Signal.from2ByteTo1Short((byte)0x01,(byte)0x00)+" ; "
									+Signal.from2ByteTo1Short((byte)0x00,(byte)0x01)+" ; "
									+Signal.from2ByteTo1Short((byte)0x01,(byte)0x01));*/
					float theta = t / 1000f;
					// int d = dati[4];
					// float t= ((byte)data[6+i*7]*255)*3.14f;
					// Common.info(7,"x="+x+" y="+y+" t="+t+" d="+d+" theta+"+theta);

					// Common.info(7,"x="+x+"y="+y+"w="+w+"h="+h+"d="+d);
					// if(w==0 && h==0 && x==0 && y==0 && d==0)
					// i=data.length;else
					// {
					ShownElement e;
					SpriteSet thisSS;
					if (d < buffer.size()) {
						// e = new ShownElement(new
						// Rectangle(x,y,w,h),buffer.get(d),0f);
						thisSS = buffer.get(d);
						// e = new ShownElement(new Point(x,y),,theta);

					} else {
						// Common.info(7,"received a block with no sprite! :( "+d);
						thisSS = new SpriteSet();
					}
					e = new ShownElement(new Point(x, y), thisSS, theta);
					screen.add(e);

					// }
				}
			}
		}
		if (signal == Signal.LEVELINFO) {
			Common.info(1, "REceived a LEVEL INFO ");
			try {
				LevelInfo i = (LevelInfo) Signal.toObject(data);
				MOTD = i.MOTD;
				resolution = i.resolution;
				scale.x = i.scaleX;
				scale.y = i.scaleY;

			} catch (IOException e) {

				e.printStackTrace();
			} catch (ClassNotFoundException e) {

				e.printStackTrace();
			}

			Common.info(1, "Client: Resolution: " + this.resolution
			+ " scale: " + scale+ "\nMODT:"+MOTD);
			isLogged=true;
			
		}if (signal == Signal.MESSAGE) {
			synchronized(serverM)
			{
				serverM=" - ";
				try {
					
					serverM = (String) Signal.toObject(data);

					//	messaggi += "life: " + i.life;
				} catch (IOException ex) {
					ex.printStackTrace();
				} catch (ClassNotFoundException ex) {
					ex.printStackTrace();
				}
				
				Common.info(1,serverM);
			}

		}
		if (signal == Signal.INFO) {
			
			try {
				Info i = (Info) Signal.toObject(data);
				messaggi = new String(i.chat);
				guns=i.guns;
				score=i.score;
				life=i.life;
				remaning=i.remaning;
				spawns=i.spawns;
				currentgun=i.currentgun;

				//	messaggi += "life: " + i.life;
			} catch (IOException ex) {
				ex.printStackTrace();
			} catch (ClassNotFoundException ex) {
				ex.printStackTrace();
			}

		}
		
		isBusy = false;
		
	}

	public void sendFlag(byte b) throws IOException {
			byte[] data = { (byte) ID, (byte) Key, b };
			// Common.info(1,"SENDING FLAG:"+data);
			DatagramPacket dp = new DatagramPacket(data, 0, data.length, addr);
			socket.send(dp);

	}

	public void updateSprites() throws IOException {
		if(isLogged)
		sendFlag(Signal.SPRITE);
	}

	public void updateScreen() throws IOException {
		if(isLogged)
		sendFlag(Signal.SCREEN);
	}

	public synchronized void sendInput(KeyFlag input) throws IOException {

		/*
		* String premuti= new String(); for (String s : i) { premuti+=s+" "; }
		*/
		if (!input.equals(lastPremuti)) {
			// Common.info(7, "sending "+input);
			// socket.send(Signal.send(input,(byte)ID,(byte)Key,Signal.INPUT,addr));
			socket.send(Signal.send(new Short(input.Key), (byte) ID,
			(byte) Key, Signal.INPUT, addr));

		} else {
			// sendFlag(Signal.INPUT);

		}
		lastPremuti = new KeyFlag(input.Key);

	}

	public synchronized void setPlayer(String n, String t) throws IOException {
		Common.info(7, "client:loggingingin");
		Login login = new Login();
		login.playerName = t;
		login.userName = n;
		socket.send(Signal.send(login, (byte) ID, (byte) Key, Signal.LOGIN,
		addr));

	} public synchronized void welcome() throws IOException {
		sendFlag(Signal.WELCOME);

	
	}


	public synchronized void getChat() throws IOException {
		if(isLogged)
		sendFlag(Signal.INFO);

	}

	public synchronized void chat(String c) throws IOException {
		socket.send(Signal.send(c, (byte) ID, (byte) Key, Signal.MESSAGE,
		addr));


	}

	public synchronized void setDirection(double x, double y)
	throws IOException {
		if(isLogged) {
			byte bx = (byte) (x * 127);
			byte by = (byte) (y * 127);
			byte[] data = { bx, by };

			socket.send(Signal.send(data, (byte) ID, (byte) Key, Signal.DIRECTION,
			addr));
		}
	}

	public void updateScreenAndSprites() throws IOException {
		// Common.info(5,"CLIENT SEND SCREEN AND SPRITE REQUEST!");
		if(isLogged)
		sendFlag(Signal.SPRITEANDSCREEN);
	}

	@SuppressWarnings("unchecked")
	public List<ShownElement> getScreen() {
		synchronized (screen)
		{
			return  screen;
		}
	}

	public String getMessages() {
		synchronized(messaggi)
		{
			return new String(messaggi);
		}
	}
	public String getMOTD() {

		return MOTD;
	}
	public String getGuns() {

		return   new String(guns);
	}
	
	public String getServerM()
	{
		synchronized (serverM)
		{
			return serverM;
		}
	}
		public void clearServerM()
	{
		synchronized (serverM)
		{
			serverM="";
		}
		
	}

}
